#pragma once

#ifndef __CANVAS_FUNCTION_H
#define __CANVAS_FUNCTION_H

#include "Objects.h"
#include "Ptr.h"

namespace Canvas
{
	template<class T>
	class Func{};

	namespace Invokers
	{
		template<class R, class... TArgs>
		class Invoker : public Object
		{
		public:
			virtual R Invoke(TArgs&& ...args) = 0;
		};

		template<class R, class... TArgs>
		class StaticInvoker : public Invoker<R, TArgs...>
		{
		protected:
			R(*function)(TArgs...);
		public:
			StaticInvoker(R(*_function)(TArgs...)) : function(_function) {}

			R Invoke(TArgs&& ...args) override
			{
				return function(ForwardValue<TArgs>(args)...);
			}
		};

		template<class C, class R, class... TArgs>
		class MemberInvoker : public Invoker<R, TArgs...>
		{
		protected:
			C* sender;
			R(C::*function)(TArgs ...args);
		public:
			MemberInvoker(C* _sender, R(C::*_function)(TArgs... args)) :
				sender(_sender), function(_function) {}
			R Invoke(TArgs&& ...args) override
			{
				return (sender->*function)(ForwardValue<TArgs>(args)...);
			}
		};

		template<class C, class R, class... TArgs>
		class ObjectInvoker : public Invoker<R, TArgs...>
		{
		protected:
			C function;
		public:
			ObjectInvoker(const C& _function) : function(_function) {}
			R Invoke(TArgs&& ... args) override
			{
				return function(ForwardValue<TArgs>(args)...);
			}
		};
		template<typename C, typename ...TArgs>
		class ObjectInvoker<C, void, TArgs...> : public Invoker<void, TArgs...>
		{
		protected:
			C function;
		public:
			ObjectInvoker(const C& _function) : 
				function(_function)
			{
			}

			void Invoke(TArgs&& ...args) override
			{
				function(ForwardValue<TArgs>(args)...);
			}
		};
	}

	template<class R, class... TArgs>
	class Func<R(TArgs...)>
	{
	protected:
		Ptr<Invokers::Invoker<R, TArgs...>> invoker;
	public:
		typedef R FunctionType(TArgs ...);
		typedef R ResultType;
		Func() {}
		Func(const Func<R(TArgs...)>& func)
		{
			invoker = func.invoker;
		}
		Func(R(*function)(TArgs...))
		{
			invoker = new Invokers::StaticInvoker<R, TArgs...>(function);
		}
		template<class C>
		Func(C* sender, R(C::*function)(TArgs...))
		{
			invoker = new Invokers::MemberInvoker<C, R, TArgs...>(sender, function);
		}

		template<class C>
		Func(const C& func)
		{
			invoker = new Invokers::ObjectInvoker<C, R, TArgs...>(func);
		}

		R operator()(TArgs... args) const
		{
			return invoker->Invoke(ForwardValue<TArgs>(args)...);
		}

		bool operator==(const Func<R(TArgs...)>& func) const
		{
			return invoker == func.invoker;
		}

		bool operator!=(const Func<R(TArgs...)>& func) const
		{
			return invoker != func.invoker;
		}

		operator bool() const
		{
			return invoker;
		}
	};
}

#endif